import Loading from '@/plugins/loading/index.vue';
import { render, createVNode } from 'vue';
import { Utils } from '@/common/utils.js';
import { USER_TYPE_FN } from '@/common/hooks';
import { ElMessage } from 'element-plus';

import type { App, Plugin } from 'vue';
interface PluginOptions {
    [property: string]: any;
}

import type { Directive } from "vue";

// 定义支持 disabled 属性的元素类型（通常为表单控件）
type DisablableElement = HTMLButtonElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
type El = HTMLElement
type Binding = {
	value?: any
	[property: string]: any
}
declare global {
	interface HTMLElement {
		/** Vue 内部事件处理器存储的 Symbol 键 */
		[key: symbol]: VueElementInternal | undefined;
		/** 自定义属性用于保存原始点击处理器 */
		__handleClick__?: (evt: Event) => void;
	}
  
	/** 表示 Vue 内部元素事件处理器的结构 */
	interface VueElementInternal {
		onClick?: {
			value: (evt: Event) => void;
		};
	}
}

export default {
    install(app: App, options: PluginOptions) {
		console.log(options.test);
		const ZERO = 0;
		/**
         * 向 app.config.globalProperties 添加全局方法 $loading
         */
		// 获取 loading/index.vue 组件的名称， 即 script 里的 name 属性
		const loadingName = Loading.name!
		// 确保 Vue 应用实例当前不存在相同属性名称，以免造成代码覆盖
		!app.config.globalProperties[loadingName] 
		&& (app.config.globalProperties[loadingName] = (status: any) => {
			// 通过 Vue 的 vNode 函数将组件实例转化为 vNode
			const vNode =  createVNode(Loading)
			// 使用 Vue 的 render 渲染函数将 vNode 渲染成真实的 DOM 并插入到 html 文档的 body
			render(vNode, document.body)
			// 调用组件暴露的方法（$loading）
			const vComp = vNode.component!
			// 此处 vCompExpos = {$loading() {...}}
			const  vCompExpos = vComp.exposed!;
			// type  x = keyof typeof vCompExpos
			vCompExpos[loadingName as keyof typeof vCompExpos](status)
		})

        /**
         * 获取字符串的px宽度
         * console.log('字符串px宽度:', proxy.$getStringTextWidth('我我我和你', '14px arial'))
         */
        !app.config.globalProperties['$getStringTextWidth']
		&& (app.config.globalProperties['$getStringTextWidth'] = (text: string, font: string = '14px arial') => {
			if(!font) return ElMessage.error('请设置字体大小和类型')
            const canvas = document.createElement('canvas');
            const context = canvas.getContext('2d')!;
			context.font = font
            return context.measureText(text).width;
        })

        /**
         * 自定义指令-防止点击按钮高频重复点击
		 * const vPreventReClick = inject('vPreventReClick')
		 * <button v-preventReClick>按钮</button>
         */
        const vPreventReClick: Directive<DisablableElement, number> = {
            mounted(el: DisablableElement, binding: Binding) {
                el.addEventListener('click', () => {
                    if(!el.disabled) {
                        el.disabled = true;
                        setTimeout(() => {
                            el.disabled = false;
                        }, binding.value || 2000);
                        //binding-value可以自行设置（默认3000毫秒），如：v-preventReClick="500"
                    }
                });
            }
        }

        /**
         * 输入框自动获取焦点
         */
        const vAutoFocus = {
            mounted(el: HTMLInputElement) {
                // 针对input元素，且兼容elementUi
                const ele = el.tagName === 'INPUT' ? el : el.querySelector('input');
                ele?.focus();
            }
        }

        /**
         * 自动点击事件
         * 如果需要点击 v-for 中的第一个，需要传递参数 index
		 * v-for="(item, index) in xxx" v-clickDown="index"
         */
        const vClickDown = {
            mounted(el: El, binding: Binding) {
                const { value } = binding;
                if(value === ZERO) return el.click();
				if(!value) el.click();
            }
        }

        /**
         * 输入框限定只能输入数字类型
         */
        const vOnlyNumber = {
            mounted(el: HTMLInputElement) {
                el.oninput = Utils.debounce(function () {
                    let ele = el.tagName === 'INPUT' ? el : el.querySelector('input')!;

                    //过滤掉数字和小数点外的其他字符
                    let eleValue = ele.value.replace(/[^0-9\.]/g, '');

                    //第一个数如果是小数点，自动修正变成"0."
                    if(eleValue.length === 1 && eleValue === '.') {
                        eleValue = '0.';
                    }

                    //第一个数如果是"0"，限定只能紧跟小数点
                    if(eleValue.length > 1 && eleValue.indexOf('0') === ZERO && eleValue.substr(1).indexOf('.') !== ZERO) eleValue = eleValue.substr(1);

                    //如果存在多个小数点，只保留第一个小数点，即截掉最后一个字符
                    let reg = /\./g;
					let points = eleValue.match(reg)!;
                    if(points !== null && points.length > 1) {
                        eleValue = eleValue.substr(0, eleValue.length -1);
						points = eleValue.match(reg)!;
                        while(true) {
                            if(points !== null && points.length > 1) {
								eleValue = eleValue.substr(0, eleValue.length -1);
								points = eleValue.match(reg)!;
							}
                            if(points.length === 1) break;
                        }
                    }
                    
                    ele.value = eleValue;
                }, 250);
            }
        }

        /**
         * 手机号码隐私保护
         */
        const vNumToConcealTEL = {
            mounted(el: El) {
                let n = el.textContent;
                if(n && n.length) {
                    const reg = /^(\d{3})\d{4}(\d{4})$/;
                    el.textContent = n.replace(reg, '$1****$2');
                }
            }
        }

        /**
         * 数字字符串转换成千分号的金额格式
         * v-numToAmountType:2="123456789.0123456"
         * arg，可选参数，代表保留小数位数，默认2，
         * value，可选参数，如果不传，则取目标 DOM 元素的 textContent
         */
        const vNumToAmountType = {
            mounted(el: El, binding: Binding) {
                let { value, arg } = binding;
                if(!value && value !== ZERO) value = el.textContent;
                let l = [],
                    r = '',
                    t = '',
                    numberStr = value + '',
                    status = false,
                    res = '';

                //如果不是数字，直接返回该字符串
                if(!/\d+/g.test(numberStr)) return numberStr;

                // 负数取正，最终结果再取负
                if(numberStr.indexOf('-') === ZERO) {
                    numberStr = numberStr.substr(1);
                    status = true;
                }
                
                arg = (Number(arg) > ZERO && Number(arg) <= 20 ? Number(arg) : 2) + '';
                numberStr = parseFloat((numberStr).replace(/[^\d\.-]/g, '')).toFixed(Number(arg));

                l = numberStr.split('.')[0].split('').reverse();
                r = numberStr.split('.')[1];

                for (var i = ZERO; i < l.length; i++) {
                    t += l[i] + ((i + 1) % 3 == ZERO && (i + 1) != l.length ? ',' : '');
                }
                if(status) {
                    res = '-' + t.split('').reverse().join('') + '.' + r;
                } else {
                    res = t.split('').reverse().join('') + '.' + r;
                }

                el.textContent = res
            }
        }

        /**
         * 千分位数字转换成 Number 类型
         */
        const vAmountToNumberType = {
            mounted(el: El, binding: Binding) {
                let { value } = binding
                if(!value && Number(value) !== ZERO) value = el.textContent;
                if(value?.length && value.indexOf(',') > ZERO) el.textContent = value.replace(/\,/g, '');
            }
        }
		
		/**
         * 仿 textarea 拖拽改变盒子高度（手动调整盒子高度）
		 * v-drag="#id"
         */
		const vDrag = {
			mounted(el: El, binding: Binding) {
				el.style.cursor = 'n-resize'
				const elTarget = document.querySelector(binding.value)
				let targetH = elTarget.offsetHeight
				el.onmousedown = function(e) {
					const Y = e.clientY
					document.onmousemove = function(e) {
						const newH = targetH + e.clientY - Y
						elTarget.style.height = newH + 'px'
					}

					document.onmouseup = function() {
						document.onmousemove = document.onmousedown = null
						targetH = elTarget.offsetHeight
					}
				}
			}
		}

		/**
		 * 用户点击事件鉴权
		 */
		const vAuth = {
			mounted(el: El, binding: Binding) {
				// const veiKey = Object.getOwnPropertySymbols(el)[0]
				const veiKey = Object.getOwnPropertySymbols(el)[0] as symbol;
				// const $el = el[veiKey]
				const $el: VueElementInternal | undefined = el[veiKey];
				if($el?.onClick) {
					if(!el.__handleClick__) el.__handleClick__ = $el.onClick.value
					// $el.onClick.value = evt => {}
					// 此处如果没有带参数 evt, Vue 会报警告
					$el.onClick.value = evt => {
						const message = '对不起，当前用户没有操作权限，请联系管理员'
						// 登录有效倒计时/秒
						const loginCountdown = Utils.getStorage({
							storageName: 'LOGIN_COUNTDOWN',
							storageType: 'localStorage'
						})
						const isValidTime = loginCountdown > 1 ? true : false
						const isValidUserType = USER_TYPE_FN({userType: 'isAdministrator'})
						// 有效登录时间内，且有操作权限的用户
						// true 放行点击事件， false 拦截
						if(isValidTime && isValidUserType) return el.__handleClick__?.(evt)
						return ElMessage({
							type: 'error',
							message
						})
					}
				}
			}
		}

		/**
		 * el-select 选择下拉框滚动触底执行加载更多的事件
		 * const vSelectLoadMore = inject('vSelectLoadMore')
		 * const selectLoadMoreObj = {
		 * 		method: () => {},
		 * 		selectIndex: n, // number
		 * 		delay: 300 // [非必选]
		 * }
		 * <el-select v-selectLoadMore="selectLoadMoreObj" ...></el-select>
		 */
		const vSelectLoadMore = {
			mounted(el: El, binding: Binding) {
				const { selectIndex, method, delay } = binding.value
				// selectIndex, 对应页面选择下拉框集合列表的 index 下标
				const selectDom = document.querySelectorAll('.el-select__popper .el-select-dropdown .el-select-dropdown__wrap')[selectIndex]
				// 适用节流模式监听 select 选择下拉框的 scroll 事件
				selectDom.addEventListener('scroll', Utils.throttle(() => {
					const H = selectDom.scrollHeight - selectDom.scrollTop <= selectDom.clientHeight
					// input 输入框输入内容时，浏览器默认会触发页面 scroll 事件，此时的 selectDom.scrollTop 为 0
					// 为避免输入内容时自动触发执行 scroll 事件， 设置 selectDom.scrollTop > 10
					if(H && selectDom.scrollTop > 10) method()
				}, delay))
			}
		}

        app.provide('vPreventReClick', vPreventReClick)         // 组件使用方式 const vPreventReClick = inject('vPreventReClick')
        app.provide('vAutoFocus', vAutoFocus)                   // 组件使用方式 const vAutoFocus = inject('vAutoFocus')
        app.provide('vClickDown', vClickDown)                   // 组件使用方式 const vClickDown = inject('vClickDown')
        app.provide('vOnlyNumber', vOnlyNumber)       			// 组件使用方式 const vOnlyNumber = inject('vOnlyNumber')
        app.provide('vNumToConcealTEL', vNumToConcealTEL)       // 组件使用方式 const vNumToConcealTEL = inject('vNumToConcealTEL')
        app.provide('vNumToAmountType', vNumToAmountType)       // 组件使用方式 const vNumToAmountType = inject('vNumToAmountType')
        app.provide('vAmountToNumberType', vAmountToNumberType) // 组件使用方式 const vAmountToNumberType = inject('vAmountToNumberType')
		app.provide('vDrag', vDrag)								// 组件使用方式 const vDrag = inject('vDrag')
		app.provide('vAuth', vAuth)								// 组件使用方式 const vAuth = inject('vAuth')
		app.provide('vSelectLoadMore', vSelectLoadMore)			// 组件使用方式 const vSelectLoadMore = inject('vSelectLoadMore')
        // app.component(alertMsg.name, alertMsg)
    }
}